package engine.video.blit;

import common.Coordinate;
import common.Rectangle;

public class ClipInfo {
	public ClipInfo() {
		Cols = 0;
		Rows = 0;
		SrcOffset = 0;
		SrcStride = 0;
		DstOffset = 0;
		DstStride = 0;
		Valid = false;
	}

	// Set to true if data is valid after a calculation.
	public boolean Valid;
	// The number of pixel columns.
	public int Cols;
	// The number of pixel rows.
	public int Rows;
	// Offset of the first pixel on the source buffer
	public int SrcOffset;
	// Source Stride
	public int SrcStride;
	// Offset of the first pixel on the destination pixel buffer.
	public int DstOffset;
	// Destination Stride
	public int DstStride;

	/**
	 * Calculate Clipping for a single pixel buffer. Results are stored in
	 * DstOffset and DstStride and duplicated in SrcOffset and SrcStride
	 * 
	 * @param buffer
	 *            The buffer to calculate for.
	 * @param rect
	 *            A rectangle relative to buffer(0,0)
	 * @return true if calculations are valid, otherwise false.
	 */
	public boolean calculate(PixelBuffer_I buffer, Rectangle rect) {
		Valid = false;
		// Trivial rejection
		if (buffer == null || rect == null)
			return false;

		// Clip buffer against rectangle.
		rect = Rectangle.Clip(
				new Rectangle(0, 0, buffer.getWidth(), buffer.getHeight()),
				rect);

		if (rect == null)
			return false;

		// Assemble the resulting clip data.
		int bufferWidth = buffer.getWidth();

		Cols = rect.Width;
		Rows = rect.Height;
		DstOffset = rect.X + (rect.Y * bufferWidth);
		DstStride = bufferWidth - Cols;
		SrcOffset = DstOffset;
		SrcStride = DstStride;
		Valid = true;

		return true;
	}
	
	public boolean calculate(PixelBuffer_I srcBuffer, Rectangle srcRect,
			PixelBuffer_I dstBuffer, Rectangle dstRect) {

		Valid = false;
		if(srcBuffer == null || dstBuffer == null) {
			return false;
		}
		
		int sx1, sx2, sy1, sy2;
		if(srcRect == null) {
			sx1 = 0;
			sx2 = srcBuffer.getWidth();
			sy1 = 0;
			sy2 = srcBuffer.getHeight();
		} else {
			sx1 = srcRect.X;
			sx2 = sx1 + srcRect.Width;
			sy1 = srcRect.Y;
			sy2 = sy1 + srcRect.Height;
		}
		
		// Clip source rect
		if (sx1 < 0)
			sx1 = 0;
		if (sx2 > srcBuffer.getWidth())
			sx2 = srcBuffer.getWidth();
		if (sy1 < 0)
			sy1 = 0;
		if (sy2 > srcBuffer.getHeight())
			sy2 = srcBuffer.getHeight();

		int dx1, dx2, dy1, dy2;
		if(dstRect == null) {
			dx1 = 0;
			dx2 = dstBuffer.getWidth();
			dy1 = 0;
			dy2 = dstBuffer.getHeight();
		} else {
			dx1 = dstRect.X;
			dx2 = dx1 + dstRect.Width;
			dy1 = dstRect.Y;
			dy2 = dy1 + dstRect.Height;
		}
		
		// Clip dest rect
		if (dx1 < 0)
			dx1 = 0;
		if (dx2 > dstBuffer.getWidth())
			dx2 = dstBuffer.getWidth();
		if (dy1 < 0)
			dy1 = 0;
		if (dy2 > dstBuffer.getHeight())
			dy2 = dstBuffer.getHeight();

		int s, d;
		// When dx1 is moved right, sx1 must move right as well.
		s = sx1;
		d = dx1;
		sx1 += d - dstRect.X;
		dx1 += s - srcRect.X;

		// When dy1 is moved down, sy1 must move down as well.
		s = sy1;
		d = dy1;
		sy1 += d - dstRect.Y;
		dy1 += s - srcRect.Y;

		// Trivial rejection.
		if (sx1 >= sx2 || sy1 >= sy2 || dx1 >= dx2 || dy1 >= dy2)
			return false;

		Cols = sx2 - sx1;
		d = dx2 - dx1;
		if (d < Cols)
			Cols = d;

		Rows = sy2 - sy1;
		d = dy2 - dy1;
		if (d < Rows)
			Rows = d;

		SrcOffset = sx1 + (sy1 * srcBuffer.getWidth());
		SrcStride = srcBuffer.getWidth() - Cols;
		DstOffset = dx1 + (dy1 * dstBuffer.getWidth());
		DstStride = dstBuffer.getWidth() - Cols;
		Valid = true;
		return true;
	}
}
